<template>
  <div class="spreadjs-container">
    <div v-if="loading" class="spreadjs-loading">
      <i class="el-icon-loading"></i>
      <p>正在加载Excel资源...</p>
    </div>
    <!-- SpreadJS容器（Designer或普通模式） -->
    <div v-show="!loading" :id="containerId" class="spreadjs-host"></div>

    <!-- 隐藏的文件输入元素，用于附件功能 -->
    <input type="file" id="choseFile" name="choseFile" style="display: none;"/>

    <!-- 附件操作对话框 -->
    <el-dialog
      title="附件操作"
      :visible.sync="attachmentDialogVisible"
      width="400px"
      center
      :close-on-click-modal="false"
      @close="handleAttachmentDialogClose"
    >
      <div style="text-align: center; padding: 20px 0;">
        <div style="margin-bottom: 30px; font-size: 14px; color: #606266;">
          <i class="el-icon-paperclip" style="font-size: 20px; margin-right: 8px; color: #409EFF;"></i>
          <span style="font-weight: 500;">{{ currentAttachmentInfo ? currentAttachmentInfo.originalName : '' }}</span>
    </div>
        <el-button 
          type="primary" 
          size="large"
          icon="el-icon-download" 
          style="width: 260px; margin-bottom: 15px;"
          @click="handleDownloadAttachment"
        >
          下载附件
        </el-button>
        <br>
        <el-button 
          type="warning" 
          size="large"
          icon="el-icon-refresh" 
          style="width: 260px; margin-bottom: 15px;"
          @click="handleUpdateAttachment"
        >
          更新附件
        </el-button>
        <br>
        <el-button 
          size="large"
          style="width: 260px;"
          @click="handleCancelAttachmentDialog"
        >
          取消
        </el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { getToken } from '@/utils/auth'

export default {
  name: 'SpreadJS',
  props: {
    // 列配置
    columns: {
      type: Array,
      default: () => []
    },
    // 数据
    data: {
      type: Array,
      default: () => []
    },
    // 是否只读
    readonly: {
      type: Boolean,
      default: false
    },
    // 是否显示工具栏
    showToolbar: {
      type: Boolean,
      default: true
    },
    // 是否使用Designer模式（完整工具栏）
    useDesigner: {
      type: Boolean,
      default: false
    },
    // 表头只读（第0行不可编辑）
    headerReadonly: {
      type: Boolean,
      default: false
    },
    // 启用列级权限（按permissionType锁定列）
    enforceColumnPermissions: {
      type: Boolean,
      default: false
    },
    // 容器ID
    containerId: {
      type: String,
      default: 'spreadjs-container'
    },
    // 是否启用分页加载
    enablePagination: {
      type: Boolean,
      default: false
    },
    // 分页大小
    pageSize: {
      type: Number,
      default: 100
    }
  },
  data() {
    return {
      spread: null,
      sheet: null,
      designer: null,
      loading: true,
      resourcesLoaded: false,
      // 附件功能相关
      hyerlinkType: 'attachfile', // 定义超链接代表的是附件
      submitFile: null, // 保存的文件
      // 附件操作对话框
      attachmentDialogVisible: false, // 对话框是否显示
      currentAttachmentInfo: null, // 当前附件信息
      currentAttachmentRow: null, // 当前附件所在行
      currentAttachmentCol: null, // 当前附件所在列
      // 分页加载相关
      currentPage: 1, // 当前页码
      totalPages: 1, // 总页数
      isLoadingMore: false, // 是否正在加载更多
      allDataLoaded: false, // 是否已加载全部数据
      loadedData: [], // 已加载的所有数据
      loadMoreTimer: null, // 加载更多的防抖定时器
      // 性能监控
      performanceTimings: {
        startTime: 0,
        cssLoadTime: 0,
        filesaverLoadTime: 0,
        scriptsLoadTime: 0,
        totalLoadTime: 0,
        initTime: 0,
        scriptTimings: []
      }
    }
  },
  watch: {
    // 监听columns变化
    columns: {
      handler(newColumns) {
        if (this.sheet && newColumns && newColumns.length > 0) {
          this.updateColumns()
        }
      },
      deep: true
    },
    // 监听data变化
    data: {
      handler(newData) {
        // 分页模式下，data变化不应该触发更新，因为数据由loadedData管理
        if (this.enablePagination) return
        
        if (this.sheet && newData && newData.length > 0) {
          this.updateData()
        }
      },
      deep: true
    }
  },
  mounted() {
    this.loadSpreadJSResources()
    
    // 添加键盘快捷键监听
    this.addKeyboardListeners()
  },
  beforeDestroy() {
    // 清理定时器
    if (this.loadMoreTimer) {
      clearTimeout(this.loadMoreTimer)
      this.loadMoreTimer = null
    }

    // 清理键盘事件监听
    this.removeKeyboardListeners()

    // 清理全局引用
    if (window.spreadjsVueInstance === this) {
      window.spreadjsVueInstance = null
    }

    // 清理SpreadJS实例
    if (this.designer) {
      try {
        this.designer.destroy()
      } catch (e) {
        console.warn('Designer销毁失败:', e)
      }
      this.designer = null
    }

    if (this.spread) {
      try {
        this.spread.destroy()
      } catch (e) {
        console.warn('Spread销毁失败:', e)
      }
      this.spread = null
    }

    this.sheet = null
  },
  methods: {
    // 动态加载SpreadJS资源
    loadSpreadJSResources() {
      // 记录开始时间
      this.performanceTimings.startTime = performance.now()
      console.log('========== SpreadJS 资源加载性能分析 ==========')
      console.log('[性能] 开始加载资源，时间:', new Date().toLocaleTimeString())

      // 如果已存在核心库，但Designer未加载且需要Designer，则继续加载Designer资源
      if (window.GC && window.GC.Spread) {
        console.log('[性能] 检测到GC.Spread已加载，跳过核心库加载')
        if (this.useDesigner && !(window.GC.Spread.Sheets && window.GC.Spread.Sheets.Designer)) {
          console.log('[性能] Designer模式但Designer未加载，开始加载Designer资源')
          const designerStartTime = performance.now()
          // 使用OSS CDN加速
          const spreadjsPath = 'https://alienware.oss-cn-beijing.aliyuncs.com/spreadjs/'
          const cssFiles = ['gc.spread.sheets.designer.18.0.0.min.css']
          cssFiles.forEach(file => {
            if (!document.querySelector(`link[href*="${file}"]`)) {
              const link = document.createElement('link')
              link.rel = 'stylesheet'
              link.type = 'text/css'
              link.href = spreadjsPath + file
              document.head.appendChild(link)
            }
          })
          const jsFiles = [
            'gc.spread.sheets.designer.resource.cn.18.0.0.min.js',
            'gc.spread.sheets.designer.all.18.0.0.min.js'
          ]
          this.loadScriptsSequentially(jsFiles.map(f => spreadjsPath + f))
            .then(() => {
              const designerLoadTime = performance.now() - designerStartTime
              console.log(`[性能] Designer资源加载完成（从OSS），耗时: ${designerLoadTime.toFixed(2)}ms`)
              this.loading = false
              this.initSpreadJS()
            })
            .catch(() => {
              this.loading = false
              this.initSpreadJS()
            })
          return
        }
        console.log('[性能] 资源已全部加载，直接初始化')
        this.loading = false
        this.initSpreadJS()
        return
      }

      // 使用OSS CDN加速（阿里云北京节点）
      const spreadjsPath = 'https://alienware.oss-cn-beijing.aliyuncs.com/spreadjs/'
      console.log('[性能] 使用OSS CDN加速资源加载')

      // 加载 CSS 文件
      const cssStartTime = performance.now()
      console.log('[性能] 开始加载CSS文件（从OSS）')
      const cssFiles = [
        'gc.spread.sheets.excel2013white.18.0.0.css'
      ]

      // 如果使用Designer模式，加载Designer CSS
      if (this.useDesigner) {
        cssFiles.push('gc.spread.sheets.designer.18.0.0.min.css')
      }

      cssFiles.forEach(file => {
        if (!document.querySelector(`link[href*="${file}"]`)) {
          const link = document.createElement('link')
          link.rel = 'stylesheet'
          link.type = 'text/css'
          link.href = spreadjsPath + file
          document.head.appendChild(link)
        }
      })
      this.performanceTimings.cssLoadTime = performance.now() - cssStartTime
      console.log(`[性能] CSS加载完成，耗时: ${this.performanceTimings.cssLoadTime.toFixed(2)}ms`)

      // 先加载 FileSaver.js (CDN)
      const filesaverStartTime = performance.now()
      console.log('[性能] 开始加载FileSaver.js (CDN)')
      this.loadScript('https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js')
        .then(() => {
          this.performanceTimings.filesaverLoadTime = performance.now() - filesaverStartTime
          console.log(`[性能] FileSaver.js加载完成，耗时: ${this.performanceTimings.filesaverLoadTime.toFixed(2)}ms`)

          // 先加载核心库（必须第一个加载）
          const coreStartTime = performance.now()
          console.log('[性能] 开始加载核心库（必须优先加载）')
          return this.loadScript(spreadjsPath + 'gc.spread.sheets.all.18.0.0.min.js')
            .then(() => {
              const coreLoadTime = performance.now() - coreStartTime
              console.log(`[性能] 核心库加载完成，耗时: ${coreLoadTime.toFixed(2)}ms`)
            })
        })
        .then(() => {
          // 第一批：并行加载基础插件（shapes 和 tablesheet，它们被其他插件依赖）
          const basePluginsStartTime = performance.now()
          console.log('[性能] 开始并行加载基础插件（shapes和tablesheet，被其他插件依赖）')

          const basePlugins = [
            'gc.spread.sheets.shapes.18.0.0.min.js',
            'gc.spread.sheets.tablesheet.18.0.0.min.js'
          ]

          return Promise.all(basePlugins.map(f => this.loadScript(spreadjsPath + f)))
            .then(() => {
              const basePluginsLoadTime = performance.now() - basePluginsStartTime
              console.log(`[性能] 基础插件加载完成，耗时: ${basePluginsLoadTime.toFixed(2)}ms`)
            })
        })
        .then(() => {
          // 第二批：并行加载所有其他插件（现在依赖关系已满足）
          const pluginsStartTime = performance.now()
          console.log('[性能] 开始并行加载所有其他插件（依赖关系已满足，可安全并行）')

          const pluginFiles = [
            'gc.spread.sheets.charts.18.0.0.min.js',
            'gc.spread.sheets.slicers.18.0.0.min.js',
            'gc.spread.sheets.print.18.0.0.min.js',
            'gc.spread.sheets.barcode.18.0.0.min.js',
            'gc.spread.sheets.pdf.18.0.0.min.js',
            'gc.spread.pivot.pivottables.18.0.0.min.js',
            'gc.spread.sheets.ganttsheet.18.0.0.min.js',
            'gc.spread.sheets.formulapanel.18.0.0.min.js',
            'gc.spread.report.reportsheet.18.0.0.min.js',
            'gc.spread.sheets.io.18.0.0.min.js',
            'gc.spread.excelio.18.0.0.min.js'
          ]

          console.log(`[性能] 并行加载 ${pluginFiles.length} 个插件文件`)

          // 使用 Promise.all 并行加载所有插件
          return Promise.all(pluginFiles.map(f => this.loadScript(spreadjsPath + f)))
            .then(() => {
              const pluginsLoadTime = performance.now() - pluginsStartTime
              console.log(`[性能] 所有插件并行加载完成，耗时: ${pluginsLoadTime.toFixed(2)}ms`)
              console.log(`[性能] 对比串行加载，预计节省时间: ${(this.performanceTimings.scriptTimings.reduce((sum, t) => {
                if (pluginFiles.some(pf => t.name.includes(pf.replace('.min.js', '')))) {
                  return sum + t.duration
                }
                return sum
              }, 0) - pluginsLoadTime).toFixed(2)}ms`)
            })
        })
        .then(() => {
          // 如果使用Designer模式，按顺序加载Designer资源（这两个有依赖关系）
          if (this.useDesigner) {
            const designerStartTime = performance.now()
            console.log('[性能] 开始按顺序加载Designer资源（有依赖关系）')

            return this.loadScript(spreadjsPath + 'gc.spread.sheets.designer.resource.cn.18.0.0.min.js')
              .then(() => this.loadScript(spreadjsPath + 'gc.spread.sheets.designer.all.18.0.0.min.js'))
              .then(() => {
                const designerLoadTime = performance.now() - designerStartTime
                console.log(`[性能] Designer资源加载完成，耗时: ${designerLoadTime.toFixed(2)}ms`)
              })
          }
          return Promise.resolve()
        })
        .then(() => {
          this.performanceTimings.totalLoadTime = performance.now() - this.performanceTimings.startTime

          console.log('========== SpreadJS 资源加载性能汇总 ==========')
          console.log(`[性能] CSS加载: ${this.performanceTimings.cssLoadTime.toFixed(2)}ms`)
          console.log(`[性能] FileSaver加载: ${this.performanceTimings.filesaverLoadTime.toFixed(2)}ms`)
          console.log(`[性能] 总加载时间: ${this.performanceTimings.totalLoadTime.toFixed(2)}ms`)

          // 输出每个脚本的加载时间详情
          if (this.performanceTimings.scriptTimings.length > 0) {
            console.log('========== 各脚本加载时间详情 ==========')
            let totalScriptTime = 0
            this.performanceTimings.scriptTimings.forEach((timing, index) => {
              console.log(`  [${index + 1}] ${timing.name}: ${timing.duration.toFixed(2)}ms`)
              totalScriptTime += timing.duration
            })
            console.log(`  合计: ${totalScriptTime.toFixed(2)}ms`)
          }
          console.log('=============================================')

          console.log('[性能] Excel资源加载完成，开始初始化')
          this.resourcesLoaded = true
          this.loading = false

          const initStartTime = performance.now()
          this.$nextTick(() => {
            this.initSpreadJS()
            this.performanceTimings.initTime = performance.now() - initStartTime
            console.log(`[性能] SpreadJS初始化完成，耗时: ${this.performanceTimings.initTime.toFixed(2)}ms`)
            console.log(`[性能] 从开始到完全初始化总耗时: ${(performance.now() - this.performanceTimings.startTime).toFixed(2)}ms`)
          })
        })
        .catch(error => {
          console.error('[性能] Excel资源加载失败:', error)
          this.$message.error('Excel资源加载失败，请刷新页面重试')
          this.loading = false
        })
    },

    // 加载单个脚本（带性能监控）
    loadScript(src) {
      const startTime = performance.now()
      const fileName = src.split('/').pop()

      return new Promise((resolve, reject) => {
        // 检查脚本是否已加载
        if (document.querySelector(`script[src="${src}"]`)) {
          const duration = performance.now() - startTime
          console.log(`[性能] ${fileName} 已缓存，跳过加载，耗时: ${duration.toFixed(2)}ms`)
          resolve()
          return
        }

        console.log(`[性能] 开始加载: ${fileName}`)
        const script = document.createElement('script')
        script.src = src
        script.type = 'text/javascript'
        script.onload = () => {
          const duration = performance.now() - startTime
          console.log(`[性能] ✓ ${fileName} 加载完成，耗时: ${duration.toFixed(2)}ms`)

          // 记录到性能统计
          this.performanceTimings.scriptTimings.push({
            name: fileName,
            duration: duration,
            url: src
          })

          resolve()
        }
        script.onerror = (error) => {
          const duration = performance.now() - startTime
          console.error(`[性能] ✗ ${fileName} 加载失败，耗时: ${duration.toFixed(2)}ms`, error)
          reject(error)
        }
        document.body.appendChild(script)
      })
    },

    // 按顺序加载多个脚本
    loadScriptsSequentially(scripts) {
      return scripts.reduce((promise, script) => {
        return promise.then(() => this.loadScript(script))
      }, Promise.resolve())
    },

    // 初始化SpreadJS
    initSpreadJS() {
      // 引入SpreadJS库
      const GC = window.GC
      if (!GC || !GC.Spread) {
        console.error('SpreadJS库未加载')
        return
      }

      // 设置授权码（必填，否则有试用期限制）
      window.LicenseKey = "E2B460Y4QSCJKHK52H55XLHZS67I1NN83VCFZ6260S108ZP2QHLd"

      // 获取宿主元素（优先根据ID，其次使用组件内第一个 .spreadjs-host）
      const hostEl = document.getElementById(this.containerId) || (this.$el && this.$el.querySelector && this.$el.querySelector('.spreadjs-host'))
      if (!hostEl) {
        // 若宿主尚未可用，延迟重试一次
        console.warn('SpreadJS 宿主元素未就绪，延迟重试:', this.containerId)
        this.$nextTick(() => {
          setTimeout(() => {
            try { this.initSpreadJS() } catch (e) { console.error(e) }
          }, 0)
        })
        return
      }

      // Designer模式：使用完整的设计器
      if (this.useDesigner && GC.Spread.Sheets.Designer) {
        const config = GC.Spread.Sheets.Designer.DefaultConfig
        
        // 注册插入附件命令
        config.commandMap = config.commandMap || {}
        if (!config.commandMap.UploadAttach) {
          config.commandMap.UploadAttach = {
            title: "插入附件",
            text: "插入附件",
            iconClass: "ribbon-button-hyperlink",
            bigButton: "true",
            commandName: "UploadAttach",
            execute: function (context, propertyName, fontItalicChecked) {
              // 直接触发文件选择，不显示弹层
              const vueInstance = window.spreadjsVueInstance
              if (vueInstance) {
                vueInstance.handleInsertAttachment()
              }
            }
          }
        }
        
        // 在"插入"选项卡添加"插入附件"按钮（检查是否已存在，避免重复添加）
        const insertTab = config.ribbon[1]
        const hasUploadAttach = insertTab.buttonGroups.some(group => group.label === "插入附件")
        if (!hasUploadAttach) {
          insertTab.buttonGroups.push({
            "label": "插入附件",
            "thumbnailClass": "welcome",
            "commandGroup": {
              "children": [{
                "direction": "vertical",
                "commands": ["UploadAttach"]
              }]
            }
          })
        }
        this.designer = new GC.Spread.Sheets.Designer.Designer(hostEl, config)
        this.spread = this.designer.getWorkbook()
        
        // 设置全局引用，供Designer命令调用
        window.spreadjsVueInstance = this
        this.sheet = this.spread.getActiveSheet()
      } else {
        // 普通模式：只使用Workbook
        this.spread = new GC.Spread.Sheets.Workbook(hostEl, {
          sheetCount: 1,
          showHorizontalScrollbar: true,
          showVerticalScrollbar: true
        })
        this.sheet = this.spread.getActiveSheet()
      }

      // 设置样式
      this.sheet.suspendPaint()

      // 初始设置较少的行数（表头+10行数据行）
      this.sheet.setRowCount(11)
      this.sheet.setColumnCount(50) // 设置足够的列数

      // 设置默认行高和列宽
      this.sheet.defaults.rowHeight = 30
      this.sheet.defaults.colWidth = 120

      // 设置表头样式
      const headerStyle = new GC.Spread.Sheets.Style()
      headerStyle.backColor = '#4472C4'
      headerStyle.foreColor = '#FFFFFF'
      headerStyle.font = 'bold 12px Arial'
      headerStyle.hAlign = GC.Spread.Sheets.HorizontalAlign.center
      headerStyle.vAlign = GC.Spread.Sheets.VerticalAlign.center

      // 设置列
      if (this.columns && this.columns.length > 0) {
        this.setupColumns(headerStyle)
      }

      // 加载数据（仅非分页模式在初始化时加载）
      if (!this.enablePagination && this.data && this.data.length > 0) {
        this.loadData()
      }

      // 设置保护模式：明确关闭保护，确保复制粘贴功能可用
        this.sheet.options.isProtected = false
      
      // 配置剪贴板选项，确保复制粘贴功能正常
      this.sheet.options.clipBoardOptions = GC.Spread.Sheets.ClipboardPasteOptions.all

      this.sheet.resumePaint()

      // 监听数据变化
      this.sheet.bind(GC.Spread.Sheets.Events.CellChanged, this.onCellChanged)

      // 监听编辑结束，自动增加行数
      this.sheet.bind(GC.Spread.Sheets.Events.EditEnded, this.onEditEnded)

      // 拦截编辑：根据需要将表头（第0行）设为只读
      this.sheet.bind(GC.Spread.Sheets.Events.EditStarting, (sender, args) => {
        if (this.headerReadonly && args && args.row === 0) {
          args.cancel = true
        }
      })

      // 监听粘贴事件，处理附件单元格的粘贴
      this.sheet.bind(GC.Spread.Sheets.Events.ClipboardPasted, this.onClipboardPasted)
      
      // 监听单元格点击事件，处理附件下载
      this.sheet.bind(GC.Spread.Sheets.Events.CellClick, this.onCellClick)

      // 注册附件命令
      this.registerAttachmentCommands()

      // 设置附件按钮事件监听
      this.$nextTick(() => {
        this.setupAttachmentListeners()
      })

      // 绑定滚动事件，支持分页加载
      if (this.enablePagination) {
        this.bindScrollEvent()
      }

      // 强制刷新布局，避免需要用户点击后才正确绘制
      try {
        if (this.designer && this.designer.refresh) this.designer.refresh()
        if (this.spread && this.spread.refresh) this.spread.refresh()
        if (this.sheet && this.sheet.repaint) this.sheet.repaint()
      } catch (e) {
        // ignore
      }

      // 通知父组件组件已就绪，便于父组件在就绪后再推送数据
      this.$emit('ready')
    },

    // 设置列
    setupColumns(headerStyle) {
      const GC = window.GC

      this.columns.forEach((col, index) => {
        // 设置表头
        this.sheet.setValue(0, index, col.fieldLabel)
        this.sheet.setStyle(0, index, headerStyle)

        // 设置列宽
        this.sheet.setColumnWidth(index, col.width || 120)

        // 当启用列权限时，不做整列锁定，改为按单元格（有数据的）锁定
        // 未启用列权限时，若列为readonly，则整列锁定（不含表头）
        if (!this.enforceColumnPermissions && col.readonly) {
          const range = new GC.Spread.Sheets.Range(1, index, -1, 1)
          this.sheet.getRange(range).locked(true)
        }

        // 恢复单元格类型（从cellTypeConfig反序列化）
        if (col.cellTypeConfig) {
          try {
            const config = JSON.parse(col.cellTypeConfig)
            
            // ComboBox下拉列表
            if (config.type === 'combobox' && config.items) {
              const comboBox = new GC.Spread.Sheets.CellTypes.ComboBox()
              
              // 确保 items 是字符串数组（只包含 text，不包含 value）
              const textItems = Array.isArray(config.items) ? config.items : []
              comboBox.items(textItems)
              comboBox.editable(config.editable !== false) // 默认可编辑
              
              // 为整列设置ComboBox（从第2行开始，不包含表头）
              for (let row = 1; row < 10000; row++) {
                this.sheet.setCellType(row, index, comboBox, GC.Spread.Sheets.SheetArea.viewport)
              }
              
              console.log(`[SpreadJS] 列 ${col.fieldLabel} 恢复ComboBox，选项:`, config.items, '可编辑:', config.editable)
            }
            // CheckBox复选框
            else if (config.type === 'checkbox') {
              const checkBox = new GC.Spread.Sheets.CellTypes.CheckBox()
              if (config.textTrue) checkBox.textTrue(config.textTrue)
              if (config.textFalse) checkBox.textFalse(config.textFalse)
              
              for (let row = 1; row < 10000; row++) {
                this.sheet.setCellType(row, index, checkBox, GC.Spread.Sheets.SheetArea.viewport)
              }
              
              console.log(`[SpreadJS] 列 ${col.fieldLabel} 恢复CheckBox`)
            }
            // 未来可扩展其他类型: button, hyperlink等
          } catch (e) {
            console.error(`[SpreadJS] 恢复列 ${col.fieldLabel} 的单元格类型失败:`, e)
          }
        }

        // 设置列数据格式（使用formatter代替CellType）
        if (col.fieldType === 'int' || col.fieldType === 'bigint') {
          // 设置数字格式
          const style = new GC.Spread.Sheets.Style()
          style.formatter = '0' // 整数格式
          style.hAlign = GC.Spread.Sheets.HorizontalAlign.right
          for (let row = 1; row < 1000; row++) {
            this.sheet.setStyle(row, index, style)
          }
        } else if (col.fieldType === 'decimal' || col.fieldType === 'double') {
          // 设置小数格式
          const style = new GC.Spread.Sheets.Style()
          style.formatter = '0.00' // 两位小数
          style.hAlign = GC.Spread.Sheets.HorizontalAlign.right
          for (let row = 1; row < 1000; row++) {
            this.sheet.setStyle(row, index, style)
          }
        }
      })

      // 冻结表头
      this.sheet.frozenRowCount(1)
    },

    // 加载数据
    loadData(data = null) {
      if (!this.sheet) return
      
      this.sheet.suspendPaint()

      // 如果传入了data参数，优先使用；否则根据分页模式选择数据源
      let dataToLoad
      if (data !== null) {
        dataToLoad = data
        console.log('[SpreadJS] 使用传入的data参数，数量:', data.length)
      } else {
      // 如果启用分页，使用loadedData；否则使用传入的data
        dataToLoad = this.enablePagination ? this.loadedData : this.data
        console.log('[SpreadJS] 使用内部数据源，数量:', dataToLoad ? dataToLoad.length : 0)
      }
      
      if (!dataToLoad || dataToLoad.length === 0) {
        console.log('[SpreadJS] 没有数据需要加载')
        this.sheet.resumePaint()
        return
      }

      // 根据数据量动态调整行数（数据行数 + 表头1行，不添加额外空行）
      const requiredRows = dataToLoad.length + 1
      const currentRows = this.sheet.getRowCount()

      // 对于分页模式，只在需要更多行时增加；非分页模式则直接设置为所需行数
      if (this.enablePagination) {
      if (requiredRows > currentRows) {
          this.sheet.setRowCount(requiredRows)
        }
      } else {
        // 非分页模式直接设置为所需行数，这样可以正确处理数据减少的情况
        this.sheet.setRowCount(requiredRows)
      }

      // 填充数据
      console.log('[SpreadJS] 开始填充数据，行数:', dataToLoad.length, '列数:', this.columns.length)
      
      if (!Array.isArray(dataToLoad)) {
        console.error('[SpreadJS] dataToLoad不是数组:', dataToLoad)
        return
      }
      
      if (!Array.isArray(this.columns)) {
        console.error('[SpreadJS] columns配置不是数组:', this.columns)
        return
      }
      
      dataToLoad.forEach((row, rowIndex) => {
        if (!row || typeof row !== 'object') {
          console.warn(`[SpreadJS] 第${rowIndex}行数据无效:`, row)
          return
        }
        
        this.columns.forEach((col, colIndex) => {
          if (!col || !col.fieldName) {
            console.warn(`[SpreadJS] 第${colIndex}列配置无效:`, col)
            return
          }
          
          const value = row[col.fieldName]
          const cellValue = value !== undefined && value !== null ? value : ''
          
          // 设置单元格值
          this.sheet.setValue(rowIndex + 1, colIndex, cellValue)
          
          // 只输出第一行的详细日志，避免日志过多
          if (rowIndex === 0) {
            console.log(`[SpreadJS] 填充数据 [${rowIndex + 1}, ${colIndex}] ${col.fieldName} = "${cellValue}"`)
          }
          
          // 检查是否是附件数据，如果是则恢复超链接
          this.restoreAttachmentIfNeeded(rowIndex + 1, colIndex, cellValue)
        })
      })
      console.log('[SpreadJS] 数据填充完成，实际填充行数:', dataToLoad.length)

      // 数据加载完成后开启筛选
      this.applyRowFilter()

      this.sheet.resumePaint()
    },

    // 应用权限（简化版：只有"有权限"和"无权限"，不再锁定单元格）
    applyCellPermissions() {
      // 权限简化：
      // - permissionType='1' 或 admin：字段可见可编辑
      // - permissionType='' 或 null：字段不可见（已在后端过滤）
      // 不再需要单元格级别的锁定，确保所有可见字段都可以编辑
      
      if (!this.sheet) return
      
      // 确保表格不受保护，所有可见字段都可以编辑
      this.sheet.options.isProtected = false
    },

    // 单元格变化事件
    onCellChanged(sender, args) {
      // 通知父组件数据变化
      this.$emit('cell-changed', {
        row: args.row,
        col: args.col,
        value: args.newValue
      })
    },

    // 编辑结束事件，自动增加行数
    onEditEnded(sender, args) {
      if (!this.sheet) return
      const currentRow = args.row
      const currentRowCount = this.sheet.getRowCount()
      
      // 如果用户编辑的行接近最后一行（剩余少于5行），自动增加5行
      if (currentRow >= currentRowCount - 5) {
        this.sheet.setRowCount(currentRowCount + 5)
      }
    },

    // 粘贴事件，处理附件单元格的粘贴
    onClipboardPasted(sender, args) {
      if (!this.sheet || !args || !args.cellRange) return
      
      const GC = window.GC
      const range = args.cellRange
      
      console.log('[SpreadJS] 粘贴事件触发，范围:', {
        row: range.row,
        col: range.col,
        rowCount: range.rowCount,
        colCount: range.colCount
      })
      
      // 延迟处理，确保粘贴操作已完成
      setTimeout(() => {
        this.sheet.suspendPaint()
        
        try {
          // 遍历粘贴范围内的所有单元格
          for (let r = range.row; r < range.row + range.rowCount; r++) {
            for (let c = range.col; c < range.col + range.colCount; c++) {
              const value = this.sheet.getValue(r, c)
              
              // 检查是否是附件数据（JSON字符串，包含 _isAttachment 标记）
              if (value && typeof value === 'string' && value.trim().startsWith('{')) {
                try {
                  const attachmentData = JSON.parse(value)
                  
                  // 如果是附件数据，恢复样式和tag
                  if (attachmentData._isAttachment && attachmentData.url && attachmentData.originalName) {
                    console.log('[SpreadJS] 检测到粘贴的附件数据，恢复样式:', {
                      row: r,
                      col: c,
                      fileName: attachmentData.originalName
                    })
                    
                    // 恢复附件（使用已有的方法）
                    this.restoreAttachmentIfNeeded(r, c, value)
                  }
                } catch (e) {
                  // 不是有效的JSON，忽略
                }
              }
            }
          }
        } catch (error) {
          console.error('[SpreadJS] 处理粘贴的附件数据时出错:', error)
        } finally {
          this.sheet.resumePaint()
          // 强制刷新视图
          this.sheet.repaint()
        }
      }, 100)  // 延迟100ms确保粘贴完成
    },
    
    // 单元格点击事件，打开附件操作对话框
    onCellClick(sender, args) {
      if (!args) return
      
      const row = args.row
      const col = args.col
      const cellTag = this.sheet.getTag(row, col)
      
      // 检查是否是附件单元格
      if (cellTag && cellTag.type === this.hyerlinkType && cellTag.fileInfo) {
        // 打开附件操作对话框
        console.log('[SpreadJS] 附件单元格点击，打开操作对话框:', cellTag.fileInfo.originalName)
        this.currentAttachmentInfo = cellTag.fileInfo
        this.currentAttachmentRow = row
        this.currentAttachmentCol = col
        this.attachmentDialogVisible = true
      }
    },

    // 下载附件
    handleDownloadAttachment() {
      if (this.currentAttachmentInfo) {
        console.log('[SpreadJS] 下载附件:', this.currentAttachmentInfo.originalName)
        this.downloadFileFromServer(
          this.currentAttachmentInfo.url, 
          this.currentAttachmentInfo.originalName
        )
        this.attachmentDialogVisible = false
      }
    },

    // 更新附件
    handleUpdateAttachment() {
      if (this.currentAttachmentRow !== null && this.currentAttachmentCol !== null) {
        console.log('[SpreadJS] 更新附件，当前单元格:', this.currentAttachmentRow, this.currentAttachmentCol)
        
        // 触发文件选择
        const choseFileInput = document.getElementById('choseFile')
        if (choseFileInput) {
          // 清空之前的文件选择
          choseFileInput.value = ''
          
          // 监听文件选择变化
          const onFileChange = async () => {
            const file = choseFileInput.files[0]
            if (file) {
              // 禁用按钮，防止重复点击
              const submitBtn = document.getElementById('submit')
              if (submitBtn) {
                submitBtn.disabled = true
                submitBtn.style.cursor = 'not-allowed'
                submitBtn.style.opacity = '0.6'
              }

              try {
                const sheet = this.spread.getActiveSheet()
                // 上传新文件，替换原附件
                await this.hasAttachFile(sheet, this.currentAttachmentRow, this.currentAttachmentCol, file)
                // 清空文件输入
                choseFileInput.value = ''
                // 关闭对话框
                this.attachmentDialogVisible = false
              } catch (error) {
                console.error('更新附件失败:', error)
              } finally {
                // 恢复按钮
                if (submitBtn) {
                  submitBtn.disabled = false
                  submitBtn.style.cursor = 'pointer'
                  submitBtn.style.opacity = '1'
                }
              }
            }
            // 移除监听器
            choseFileInput.removeEventListener('change', onFileChange)
          }
          
          choseFileInput.addEventListener('change', onFileChange)
          // 触发文件选择
          choseFileInput.click()
        }
      }
    },

    // 插入附件（简化版，直接弹出文件选择）
    handleInsertAttachment() {
      console.log('[SpreadJS] 插入附件')
      
      // 获取当前选中的单元格
      const sheet = this.spread.getActiveSheet()
      const row = sheet.getActiveRowIndex()
      const col = sheet.getActiveColumnIndex()
      
      console.log('[SpreadJS] 当前选中单元格:', row, col)
      
      // 触发文件选择
      const choseFileInput = document.getElementById('choseFile')
      if (choseFileInput) {
        // 清空之前的文件选择
        choseFileInput.value = ''
        
        // 监听文件选择变化
        const onFileChange = async () => {
          const file = choseFileInput.files[0]
          if (!file) {
            console.log('[SpreadJS] 用户取消了文件选择')
            choseFileInput.removeEventListener('change', onFileChange)
            return
          }

          console.log('[SpreadJS] 用户选择了文件:', file.name)

          try {
            // 上传文件到当前选中的单元格
            await this.hasAttachFile(sheet, row, col, file)
            // 清空文件输入
            choseFileInput.value = ''
            console.log('[SpreadJS] 插入附件成功')
          } catch (error) {
            console.error('插入附件失败:', error)
            this.$message.error('插入附件失败: ' + (error.message || '未知错误'))
          }
          
          // 移除监听器
          choseFileInput.removeEventListener('change', onFileChange)
        }
        
        choseFileInput.addEventListener('change', onFileChange)
        // 触发文件选择
        choseFileInput.click()
      } else {
        console.error('[SpreadJS] 找不到文件输入元素')
        this.$message.error('文件选择功能不可用')
      }
    },

    // 取消附件操作对话框
    handleCancelAttachmentDialog() {
      this.attachmentDialogVisible = false
      this.currentAttachmentInfo = null
      this.currentAttachmentRow = null
      this.currentAttachmentCol = null
    },

    // 关闭对话框时清理
    handleAttachmentDialogClose() {
      this.currentAttachmentInfo = null
      this.currentAttachmentRow = null
      this.currentAttachmentCol = null
    },

    // ==================== 键盘快捷键相关方法 ====================

    // 添加键盘事件监听
    addKeyboardListeners() {
      // 绑定键盘事件到document，确保在SpreadJS获得焦点时也能响应
      document.addEventListener('keydown', this.handleKeyDown)
      console.log('[SpreadJS] 键盘快捷键监听已添加')
    },

    // 移除键盘事件监听
    removeKeyboardListeners() {
      document.removeEventListener('keydown', this.handleKeyDown)
      console.log('[SpreadJS] 键盘快捷键监听已移除')
    },

    // 处理键盘按下事件
    handleKeyDown(event) {
      // 检查当前焦点是否在SpreadJS容器内或其相关元素上
      const activeElement = document.activeElement
      const spreadjsContainer = document.getElementById(this.containerId)
      
      const isSpreadJSFocused = spreadjsContainer && (
        spreadjsContainer.contains(activeElement) || 
        activeElement === spreadjsContainer ||
        activeElement === document.body || // 当SpreadJS获得焦点时，activeElement可能是body
        (activeElement && activeElement.closest && activeElement.closest('.spreadjs-container'))
      )
      
      if (!isSpreadJSFocused) {
        return // 如果焦点不在SpreadJS上，不处理任何快捷键
      }
      
      // Ctrl+S 或 Cmd+S：保存
      if ((event.ctrlKey || event.metaKey) && event.key === 's') {
        // 阻止浏览器默认的保存行为
        event.preventDefault()
        event.stopPropagation()
        
        console.log('[SpreadJS] 检测到Ctrl+S快捷键，触发保存')
        
        // 触发保存事件，通知父组件
        this.$emit('save-shortcut')
        
        return false
      }
      
      // F5：刷新
      if (event.key === 'F5') {
        // 阻止浏览器默认的刷新行为
        event.preventDefault()
        event.stopPropagation()
        
        console.log('[SpreadJS] 检测到F5快捷键，触发刷新')
        
        // 触发刷新事件，通知父组件
        this.$emit('refresh-shortcut')
        
        return false
      }
      
      // Ctrl+R 或 Cmd+R：刷新（备用快捷键）
      if ((event.ctrlKey || event.metaKey) && event.key === 'r') {
        // 阻止浏览器默认的刷新行为
        event.preventDefault()
        event.stopPropagation()
        
        console.log('[SpreadJS] 检测到Ctrl+R快捷键，触发刷新')
        
        // 触发刷新事件，通知父组件
        this.$emit('refresh-shortcut')
        
        return false
      }
    },

    // 获取数据
    getData() {
      const result = []
      const rowCount = this.sheet.getRowCount()

      for (let row = 1; row < rowCount; row++) {
        // 检查该行是否有数据或者是已存在的记录（有ID）
        let hasData = false
        const rowData = {}

        // 检查是否为已存在的记录（从loadedData获取ID）
        const existingRecord = this.loadedData && this.loadedData[row - 1]
        const hasId = existingRecord && existingRecord.id

        this.columns.forEach((col, colIndex) => {
          // 优先检查是否为附件（根据单元格Tag）
          const cellTag = this.sheet.getTag(row, colIndex)
          if (cellTag && cellTag.type === this.hyerlinkType && cellTag.fileInfo) {
            const attachmentData = {
              _isAttachment: true,
              url: cellTag.fileInfo.url,
              originalName: cellTag.fileInfo.originalName,
              fullUrl: cellTag.fileInfo.fullUrl
            }
            rowData[col.fieldName] = JSON.stringify(attachmentData)
            hasData = true
        } else {
          const value = this.sheet.getValue(row, colIndex)
          if (value !== null && value !== undefined && value !== '') {
            hasData = true
          }
          
          
          // 对于空值，明确设置为 null，以便后端能够识别并更新
          rowData[col.fieldName] = (value === null || value === undefined || value === '') ? null : value
        }
        })

        // 如果该行有数据，或者是已存在的记录（需要支持置空操作）
        if (hasData || hasId) {
          // 如果是已存在的记录，保留ID
          if (hasId) {
            rowData.id = existingRecord.id
          }
          result.push(rowData)
        }
      }

      return result
    },

    // 清空数据（保留表头行）
    clearData() {
      if (!this.sheet) return
      const GC = window.GC
      const totalRows = this.sheet.getRowCount()
      const totalCols = this.sheet.getColumnCount()
      const dataRowCount = Math.max(0, totalRows - 1)
      if (dataRowCount <= 0 || totalCols <= 0) return
      const area = GC && GC.Spread && GC.Spread.Sheets ? GC.Spread.Sheets.SheetArea.viewport : 3
      const storage = GC && GC.Spread && GC.Spread.Sheets ? GC.Spread.Sheets.StorageType.data : 1
      this.sheet.clear(1, 0, dataRowCount, totalCols, area, storage)
    },

    // 重置表格（清空数据并重置行数和分页状态）
    resetSheet() {
      if (!this.sheet) return

      // 清空数据
      this.clearData()

      // 重置行数到初始状态（表头1行 + 基础10行）
      const initialRows = 11
      this.sheet.setRowCount(initialRows)

      // 重置分页相关状态
      if (this.enablePagination) {
        this.resetPagination()
      }
    },

    // 刷新数据
    refreshData(data) {
      this.clearData()
      this.data.splice(0, this.data.length, ...data)
      this.loadData()
    },

    // 添加行
    addRow() {
      const rowCount = this.sheet.getRowCount()
      this.sheet.addRows(rowCount, 1)
    },

    // 删除选中行
    deleteSelectedRows() {
      const selections = this.sheet.getSelections()
      if (selections && selections.length > 0) {
        const selection = selections[0]
        if (selection.row > 0) { // 不能删除表头
          this.sheet.deleteRows(selection.row, selection.rowCount)
        }
      }
    },

    // 导出Excel
    exportExcel(fileName) {
      const GC = window.GC
      if (!GC || !GC.Spread || !GC.Spread.Excel) {
        console.error('Excel IO模块未加载')
        this.$message.error('Excel导出功能未加载')
        return
      }

      // 导出前处理附件单元格，将其转换为可点击的超链接
      this.prepareAttachmentsForExport()

      const excelIO = new GC.Spread.Excel.IO()
      const json = this.spread.toJSON()

      excelIO.save(
        json,
        (blob) => {
          // 优先使用 FileSaver.js 的 saveAs
          if (window.saveAs) {
            window.saveAs(blob, fileName || 'export.xlsx')
          } else {
            // 降级方案：使用传统方式下载
            const url = window.URL.createObjectURL(blob)
            const a = document.createElement('a')
            a.style.display = 'none'
            a.href = url
            a.target = '_blank'
            a.download = fileName || 'export.xlsx'
            document.body.appendChild(a)
            a.click()
            document.body.removeChild(a)
            window.URL.revokeObjectURL(url)
          }
          
          // 导出完成后恢复附件单元格的原始状态
          this.restoreAttachmentsAfterExport()
        },
        (error) => {
          console.error('导出失败:', error)
          this.$message.error('Excel导出失败')
          // 即使导出失败也要恢复状态
          this.restoreAttachmentsAfterExport()
        }
      )
    },

    // 导出前准备：将附件单元格转换为Excel超链接
    prepareAttachmentsForExport() {
      if (!this.sheet) return

      const GC = window.GC
      const rowCount = this.sheet.getRowCount()
      const colCount = this.sheet.getColumnCount()

      console.log('[SpreadJS] 准备导出，处理附件单元格为超链接')

      for (let row = 0; row < rowCount; row++) {
        for (let col = 0; col < colCount; col++) {
          const cellTag = this.sheet.getTag(row, col)
          
          // 检查是否是附件单元格
          if (cellTag && cellTag.type === this.hyerlinkType && cellTag.fileInfo) {
            const fileInfo = cellTag.fileInfo
            
            // 设置单元格值为文件名（这样导出时显示文件名而非JSON）
            this.sheet.setValue(row, col, fileInfo.originalName)
            
            // 设置超链接（导出到Excel文件时会保留）
            this.sheet.setHyperlink(row, col, {
              url: fileInfo.fullUrl,
              tooltip: '点击下载: ' + fileInfo.originalName
            })
            
            console.log(`[SpreadJS] 附件单元格 [${row},${col}] 已设置为超链接:`, fileInfo.fullUrl)
          }
        }
      }
    },

    // 导出后恢复：将超链接恢复为原始的附件显示样式
    restoreAttachmentsAfterExport() {
      if (!this.sheet) return

      const GC = window.GC
      const rowCount = this.sheet.getRowCount()
      const colCount = this.sheet.getColumnCount()

      console.log('[SpreadJS] 导出完成，恢复附件单元格样式')

      for (let row = 0; row < rowCount; row++) {
        for (let col = 0; col < colCount; col++) {
          const cellTag = this.sheet.getTag(row, col)
          
          // 检查是否是附件单元格
          if (cellTag && cellTag.type === this.hyerlinkType && cellTag.fileInfo) {
            const fileInfo = cellTag.fileInfo
            
            // 移除Excel超链接，恢复为原始样式
            this.sheet.setHyperlink(row, col, null)
            
            // 恢复原始显示样式
            this.sheet.setValue(row, col, JSON.stringify({
              _isAttachment: true,
              url: fileInfo.url,
              originalName: fileInfo.originalName,
              fullUrl: fileInfo.fullUrl
            }))
            
            this.sheet.setText(row, col, fileInfo.originalName)
            
            // 恢复模拟超链接样式
            const style = new GC.Spread.Sheets.Style()
            style.foreColor = '#0066cc'
            style.textDecoration = GC.Spread.Sheets.TextDecorationType.underline
            style.cursor = 'pointer'
            this.sheet.setStyle(row, col, style)
          }
        }
      }
    },

    // 导入Excel
    importExcel(file) {
      const GC = window.GC
      const excelIO = new GC.Spread.Excel.IO()

      excelIO.open(file, (json) => {
        this.spread.fromJSON(json)
        this.sheet = this.spread.getActiveSheet()
        this.$emit('import-success')
      }, (error) => {
        console.error('导入失败:', error)
        this.$emit('import-error', error)
      })
    },

    // 更新列（用于异步数据加载）
    updateColumns() {
      if (!this.sheet || !this.columns || this.columns.length === 0) {
        return
      }

      this.sheet.suspendPaint()

      // 设置表头样式
      const GC = window.GC
      const headerStyle = new GC.Spread.Sheets.Style()
      headerStyle.backColor = '#4472C4'
      headerStyle.foreColor = '#FFFFFF'
      headerStyle.font = 'bold 12px Arial'
      headerStyle.hAlign = GC.Spread.Sheets.HorizontalAlign.center
      headerStyle.vAlign = GC.Spread.Sheets.VerticalAlign.center

      // 设置列
      this.setupColumns(headerStyle)

      this.sheet.resumePaint()

      // 列更新后，不自动刷新数据，避免清空分页数据
    },

    // 更新数据（用于异步数据加载）
    updateData() {
      // 分页模式下不使用此方法
      if (this.enablePagination) return
      
      if (!this.sheet || !this.data || this.data.length === 0) {
        return
      }

      this.sheet.suspendPaint()

      // 先清空现有数据（保留表头）
      this.clearData()

      // 加载新数据
      this.loadData()

      this.sheet.resumePaint()
    },

    // ==================== 附件功能相关方法 ====================

    // 注册附件命令
    registerAttachmentCommands() {
      if (!this.spread) return
      const GC = window.GC

      // 注册清除附件文件的命令
      this.spread.commandManager().register('removeAttachFile', {
        canUndo: false,
        execute: (context, options, isUndo) => {
          const { sheet, row, col } = options
          const cellTag = sheet.getTag(row, col)
          if (cellTag && cellTag.type === this.hyerlinkType) {
            sheet.clear(
              row,
              col,
              1,
              1,
              GC.Spread.Sheets.SheetArea.viewport,
              GC.Spread.Sheets.StorageType.data | GC.Spread.Sheets.StorageType.tag
            )
            sheet.refresh()
            this.$message.success('附件已清除')
          } else {
            this.$message.warning('当前单元格无附件')
          }
        }
      })

      // 注册下载附件文件的命令
      this.spread.commandManager().register('downloadAttachFile', {
        canUndo: false,
        execute: (context, options, isUndo) => {
          const sheet = context.getActiveSheet()
          const row = sheet.getActiveRowIndex()
          const col = sheet.getActiveColumnIndex()
          const cellTag = sheet.getTag(row, col)

          if (cellTag && cellTag.type === this.hyerlinkType) {
            // 优先使用 fullUrl 直接下载/打开，其次回退到服务端下载接口
            if (cellTag.fileInfo) {
              const fullUrl = cellTag.fileInfo.fullUrl
              const url = cellTag.fileInfo.url
              if (fullUrl) {
                try {
                  const link = document.createElement('a')
                  link.href = fullUrl
                  link.download = cellTag.fileInfo.originalName || ''
                  link.target = '_blank'
                  link.style.display = 'none'
                  document.body.appendChild(link)
                  link.click()
                  document.body.removeChild(link)
                } catch (e) {
                  window.open(fullUrl, '_blank')
                }
              } else if (url) {
                this.downloadFileFromServer(url, cellTag.fileInfo.originalName)
              } else {
                this.$message.error('文件信息不完整')
              }
            } else {
              this.$message.error('文件信息不完整')
            }
          } else {
            this.$message.warning('当前单元格无附件')
          }
        }
      })
    },

    // 将文件附加到单元格（上传到服务器）
    async hasAttachFile(sheet, row, col, file) {
      const GC = window.GC
      
      // 显示全屏加载遮罩
      const loading = this.$loading({
        lock: true,
        text: `正在上传文件：${file.name}（0%）`,
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)'
      })

      try {
        // 上传文件到服务器，传递进度回调
        const fileInfo = await this.uploadFileToServer(file, (percent) => {
          // 更新上传进度
          loading.text = `正在上传文件：${file.name}（${percent}%）`
        })
        
        // 将文件信息存储为JSON格式，以便保存到数据库后能恢复
        const attachmentData = {
          _isAttachment: true,
          url: fileInfo.url,
          originalName: fileInfo.originalName,
          fullUrl: fileInfo.fullUrl
        }
        
        // 设置单元格值为JSON字符串（用于保存到数据库）
        sheet.setValue(row, col, JSON.stringify(attachmentData))
        
        // 存储文件信息到单元格标签
        sheet.setTag(row, col, {
          type: this.hyerlinkType,
          fileInfo: fileInfo
        })
        
        // 强制显示文件名而非原始JSON
        sheet.setText(row, col, fileInfo.originalName)

        // 设置单元格样式：模拟超链接外观（蓝色、下划线、手型光标）
        const style = new GC.Spread.Sheets.Style()
        style.foreColor = '#0066cc'  // 蓝色文字
        style.textDecoration = GC.Spread.Sheets.TextDecorationType.underline  // 下划线
        style.cursor = 'pointer'  // 手型光标
        sheet.setStyle(row, col, style)
        
        this.$message.success('文件上传成功')
      } catch (error) {
        console.error('文件上传失败:', error)
        this.$message.error('文件上传失败: ' + (error.message || '未知错误'))
      } finally {
        // 关闭加载遮罩
        loading.close()
      }
    },

    // 设置附件按钮事件监听（简化版，不再需要监听submit和cancel按钮）
    setupAttachmentListeners() {
      // 由于插入附件现在直接使用handleInsertAttachment方法
      // 这个方法保留为空，避免其他地方的调用出错
      console.log('[SpreadJS] 附件监听器设置完成（简化版）')
    },

    // 验证文件（暂时不做任何限制）
    validateFile(file) {
          if (!file) {
        return { valid: false, message: '请选择文件' }
      }

      // 暂时不限制文件格式和大小
      return { valid: true }
    },
    
    // 上传文件到服务器
    uploadFileToServer(file, onProgress) {
      return new Promise((resolve, reject) => {
        // 验证文件
        const validation = this.validateFile(file)
        if (!validation.valid) {
          reject(new Error(validation.message))
          return
        }

        const formData = new FormData()
        formData.append('file', file)
        
        const xhr = new XMLHttpRequest()
        // 获取 baseURL：优先使用环境变量，否则使用当前域名的相对路径
        let baseURL = process.env.VUE_APP_BASE_API
        if (!baseURL || baseURL === 'undefined') {
          // 如果环境变量未设置，使用相对路径（适用于前后端同域部署）
          baseURL = window.location.origin
        }
        const uploadUrl = baseURL + '/tool/spreadjs/upload'
        console.log('[SpreadJS] 上传地址:', uploadUrl)
        xhr.open('POST', uploadUrl, true)
        
        // 添加认证token
        const token = getToken()
        if (token) {
          xhr.setRequestHeader('Authorization', 'Bearer ' + token)
        }

        // 监听上传进度
        if (xhr.upload && onProgress) {
          xhr.upload.onprogress = (event) => {
            if (event.lengthComputable) {
              const percent = Math.round((event.loaded / event.total) * 100)
              onProgress(percent)
            }
          }
        }
        
        xhr.onload = () => {
          if (xhr.status === 200) {
            try {
              const response = JSON.parse(xhr.responseText)
              if (response.code === 200) {
                resolve({
                  url: response.fileName,  // 文件路径，用于下载
                  originalName: response.originalFilename,  // 原始文件名
                  fullUrl: response.url  // 完整URL
                })
              } else {
                reject(new Error(response.msg || '上传失败'))
              }
            } catch (e) {
              reject(new Error('解析响应失败'))
            }
          } else {
            reject(new Error('上传请求失败'))
          }
        }
        
        xhr.onerror = () => {
          reject(new Error('网络错误'))
        }
        
        xhr.send(formData)
      })
    },
    
    // 从服务器下载文件
    downloadFileFromServer(fileUrl, fileName) {
      try {
        // 获取 baseURL：优先使用环境变量，否则使用当前域名的相对路径
        let baseURL = process.env.VUE_APP_BASE_API
        if (!baseURL || baseURL === 'undefined') {
          // 如果环境变量未设置，使用相对路径（适用于前后端同域部署）
          baseURL = window.location.origin
        }
        const downloadUrl = baseURL + '/tool/spreadjs/download?resource=' + encodeURIComponent(fileUrl)
        console.log('[SpreadJS] 下载地址:', downloadUrl)
        
        // 创建隐藏的a标签进行下载
        const link = document.createElement('a')
        link.href = downloadUrl
        link.download = fileName || '附件'
        link.style.display = 'none'
        document.body.appendChild(link)
        link.click()
        document.body.removeChild(link)
        
        this.$message.success('开始下载文件')
      } catch (error) {
        console.error('文件下载失败:', error)
        this.$message.error('文件下载失败')
      }
    },
    
    // 检查并恢复附件（从数据库加载数据时调用）
    restoreAttachmentIfNeeded(row, col, value) {
      if (!value) return

      try {
        // 支持字符串或对象两种形式
        let attachmentData = null
        if (typeof value === 'string') {
          attachmentData = JSON.parse(value)
        } else if (typeof value === 'object') {
          attachmentData = value
        }
        
        // 检查是否是附件数据
        if (attachmentData && attachmentData._isAttachment && attachmentData.url && attachmentData.originalName) {
          const GC = window.GC
          
          // 存储文件信息到单元格标签
          this.sheet.setTag(row, col, {
            type: this.hyerlinkType,
            fileInfo: {
              url: attachmentData.url,
              originalName: attachmentData.originalName,
              fullUrl: attachmentData.fullUrl
            }
          })
          
          // 如果当前单元格值是对象，统一存为JSON字符串，便于保存
          if (typeof value === 'object') {
            this.sheet.setValue(row, col, JSON.stringify(attachmentData))
          }

          // 强制显示文件名而非原始JSON
          this.sheet.setText(row, col, attachmentData.originalName)

          // 设置单元格样式：模拟超链接外观（蓝色、下划线、手型光标）
          const style = new GC.Spread.Sheets.Style()
          style.foreColor = '#0066cc'  // 蓝色文字
          style.textDecoration = GC.Spread.Sheets.TextDecorationType.underline  // 下划线
          style.cursor = 'pointer'  // 手型光标
          this.sheet.setStyle(row, col, style)
        }
      } catch (e) {
        // 不是JSON格式，忽略
      }
    },

    // 清除附件（对外暴露的方法）
    removeAttachment() {
      if (!this.spread) return
      const sheet = this.spread.getActiveSheet()
      const row = sheet.getActiveRowIndex()
      const col = sheet.getActiveColumnIndex()
      this.spread.commandManager().execute({
        cmd: 'removeAttachFile',
        sheet,
        row,
        col
      })
    },

    // 保存文件（对外暴露的方法）
    saveFile() {
      this.submitFile = this.spread.toJSON()
      this.$message.success('文件已保存到内存')
    },

    // 加载已保存文件（对外暴露的方法）
    loadSubmitFile() {
      if (this.submitFile) {
        this.spread.fromJSON(this.submitFile)
        this.$message.success('文件已加载')
      } else {
        this.$message.warning('没有已保存的文件')
      }
    },

    // ==================== 分页加载相关方法 ====================

    // 绑定滚动事件
    bindScrollEvent() {
      if (!this.sheet) return
      const GC = window.GC
      
      // 监听多种可能导致视图变化的事件
      this.sheet.bind(GC.Spread.Sheets.Events.TopRowChanged, this.checkAndLoadMore) // 滚动条/键盘导航
      this.sheet.bind(GC.Spread.Sheets.Events.SelectionChanged, this.checkAndLoadMore) // 选中行变化（包括END键）
    },

    // 检查是否需要加载更多数据
    checkAndLoadMore(sender, args) {
      console.log('checkAndLoadMore 触发', {
        isLoadingMore: this.isLoadingMore,
        allDataLoaded: this.allDataLoaded,
        enablePagination: this.enablePagination,
        loadedDataLength: this.loadedData ? this.loadedData.length : 0,
        currentPage: this.currentPage,
        totalPages: this.totalPages,
        totalRecords: this.totalRecords
      })
      
      if (this.isLoadingMore || this.allDataLoaded || !this.enablePagination) return
      if (!this.sheet || !this.loadedData || this.loadedData.length === 0) return

      // 使用防抖，避免频繁触发
      if (this.loadMoreTimer) {
        clearTimeout(this.loadMoreTimer)
      }

      this.loadMoreTimer = setTimeout(() => {
        const sheet = this.sheet
        if (!sheet) return
        
        // 如果没有更多数据要加载，直接返回
        if (this.currentPage >= this.totalPages) {
          this.allDataLoaded = true
          console.log('已加载全部数据，当前页:', this.currentPage, '总页数:', this.totalPages)
          return
        }

        // 获取可见区域的底部行
        const viewportBottomRow = sheet.getViewportBottomRow(1)
        
        // 获取当前选中的行（考虑END键跳转）
        let selectedRow = 0
        const selections = sheet.getSelections()
        if (selections && selections.length > 0) {
          const selection = selections[0]
          selectedRow = selection.row + selection.rowCount - 1
        }
        
        // 取可见底部行和选中行的最大值
        const targetRow = Math.max(viewportBottomRow, selectedRow)
        
        // 获取当前已加载的数据行数
        const dataRowCount = this.loadedData.length
        
        console.log('检查加载条件:', {
          viewportBottomRow,
          selectedRow,
          targetRow,
          dataRowCount,
          threshold: dataRowCount - 15,
          shouldLoad: targetRow >= dataRowCount - 15,
          currentPage: this.currentPage,
          totalPages: this.totalPages
        })
        
        // 当目标行接近已加载数据末尾时触发（最后15行内）
        // targetRow 是选中行或可见底行的最大值，dataRowCount 是数据条数
        // 因为第0行是表头，所以最后一条数据在第 dataRowCount 行
        if (targetRow >= dataRowCount - 15) {
          console.log('✅ 触发加载下一页')
          this.loadNextPage()
        } else {
          console.log('❌ 未达到加载条件')
        }
      }, 200) // 200ms防抖
    },

    // 加载下一页
    loadNextPage() {
      console.log('loadNextPage 被调用', {
        isLoadingMore: this.isLoadingMore,
        allDataLoaded: this.allDataLoaded,
        currentPage: this.currentPage
      })
      
      if (this.isLoadingMore || this.allDataLoaded) return

      this.isLoadingMore = true
      this.currentPage++
      
      console.log('🔄 开始加载第', this.currentPage, '页')

      // 通知父组件加载下一页数据
      this.$emit('load-more', {
        page: this.currentPage,
        pageSize: this.pageSize,
        callback: this.appendPageData
      })
    },

    // 追加分页数据
    appendPageData(newData, total) {
      console.log('📦 appendPageData 被调用', {
        newDataLength: newData ? newData.length : 0,
        total,
        currentLoadedLength: this.loadedData.length
      })
      
      if (!newData || newData.length === 0) {
        this.allDataLoaded = true
        this.isLoadingMore = false
        console.log('⚠️ 没有新数据，标记为全部加载完成')
        return
      }

      // 计算起始行（在追加前计算，因为此时loadedData还是旧数据）
      // 行索引：0是表头，1是第一行数据，所以起始行 = 已加载数据长度 + 1
      const startRow = this.loadedData.length + 1
      console.log('📍 起始行:', startRow)

      // 追加数据到已加载数据列表
      this.loadedData = this.loadedData.concat(newData)
      console.log('✅ 数据已追加到 loadedData，新长度:', this.loadedData.length)

      // 计算总页数
      if (total !== undefined) {
        this.totalPages = Math.ceil(total / this.pageSize)
      }

      // 追加数据到表格
      this.sheet.suspendPaint()
      
      // 先确保有足够的行数（不添加额外空行）
      const requiredRows = this.loadedData.length + 1
      const currentRows = this.sheet.getRowCount()
      if (requiredRows > currentRows) {
        this.sheet.setRowCount(requiredRows)
        console.log('📏 扩展行数:', currentRows, '=>', requiredRows)
      }

      // 填充新数据
      console.log('开始填充数据，列数:', this.columns.length, '新数据行数:', newData.length)
      let filledRowCount = 0
      let sampleData = null
      let emptyRowCount = 0
      newData.forEach((row, index) => {
        const actualRow = startRow + index
        let hasData = false
        if (index === 0) {
          sampleData = { row: actualRow, data: {} }
        }
        this.columns.forEach((col, colIndex) => {
          const value = row[col.fieldName]
          const cellValue = value !== undefined ? value : ''
          
          // 设置单元格值
          this.sheet.setValue(actualRow, colIndex, cellValue)
          
          // 为新行设置单元格类型（ComboBox、CheckBox等）
          this.applyCellTypeForRow(actualRow, colIndex, col)
          
          // 检查是否是附件数据，如果是则恢复超链接
          this.restoreAttachmentIfNeeded(actualRow, colIndex, cellValue)
          
          if (value !== undefined && value !== null && value !== '') {
            hasData = true
          }
          if (index === 0) {
            sampleData.data[col.fieldLabel] = value
          }
        })
        if (!hasData) {
          emptyRowCount++
        }
        filledRowCount++
      })
      console.log('✅ 新数据已填充到表格，从第', startRow, '行到第', (startRow + filledRowCount - 1), '行')
      console.log('第一行样例数据:', sampleData)
      console.log('⚠️ 空行数量:', emptyRowCount, '/', newData.length)

      // 追加数据后更新筛选
      this.applyRowFilter()

      this.sheet.resumePaint()
      this.isLoadingMore = false

      // 检查是否已加载完全部数据
      if (this.currentPage >= this.totalPages) {
        this.allDataLoaded = true
        console.log('🎉 全部', this.totalPages, '页数据已加载完成')
      }
    },

    // 为指定行列设置单元格类型（用于分页追加数据）
    applyCellTypeForRow(row, col, columnConfig) {
      if (!columnConfig || !columnConfig.cellTypeConfig) {
        return
      }

      const GC = window.GC
      if (!GC || !GC.Spread || !GC.Spread.Sheets) {
        return
      }

      try {
        const config = JSON.parse(columnConfig.cellTypeConfig)
        
        // ComboBox下拉列表
        if (config.type === 'combobox' && config.items) {
          const comboBox = new GC.Spread.Sheets.CellTypes.ComboBox()
          
          // 确保 items 是字符串数组（只包含 text，不包含 value）
          const textItems = Array.isArray(config.items) ? config.items : []
          comboBox.items(textItems)
          comboBox.editable(config.editable !== false) // 默认可编辑
          
          // 为指定单元格设置ComboBox
          this.sheet.setCellType(row, col, comboBox, GC.Spread.Sheets.SheetArea.viewport)
          
          console.log(`[SpreadJS] 为行${row}列${col}设置ComboBox，选项:`, config.items)
        }
        // CheckBox复选框
        else if (config.type === 'checkbox') {
          const checkBox = new GC.Spread.Sheets.CellTypes.CheckBox()
          if (config.textTrue) checkBox.textTrue(config.textTrue)
          if (config.textFalse) checkBox.textFalse(config.textFalse)
          
          // 为指定单元格设置CheckBox
          this.sheet.setCellType(row, col, checkBox, GC.Spread.Sheets.SheetArea.viewport)
          
          console.log(`[SpreadJS] 为行${row}列${col}设置CheckBox`)
        }
      } catch (e) {
        console.error(`[SpreadJS] 为行${row}列${col}设置单元格类型失败:`, e)
      }
    },

    // 初始化分页数据（供父组件调用）
    initPaginationData(firstPageData, total) {
      // 设置分页状态
      this.loadedData = firstPageData || []
      this.currentPage = 1
      this.totalPages = Math.ceil(total / this.pageSize)
      this.allDataLoaded = this.loadedData.length >= total
      
      // 等待 columns 更新后再加载数据
      this.$nextTick(() => {
        if (this.columns && this.columns.length > 0) {
          this.loadData()
        }
      })
    },


    // 重置分页状态
    resetPagination() {
      this.currentPage = 1
      this.totalPages = 1
      this.isLoadingMore = false
      this.allDataLoaded = false
      this.loadedData = []
    },

    // 应用行筛选（所有列默认开启筛选下拉）
    applyRowFilter() {
      if (!this.sheet || !this.columns || this.columns.length === 0) return
      
      const GC = window.GC
      if (!GC || !GC.Spread || !GC.Spread.Sheets || !GC.Spread.Sheets.Filter) return

      const colCount = this.columns.length
      const dataRowCount = this.enablePagination ? this.loadedData.length : this.data.length
      
      // 创建筛选范围：从第0行（表头）开始，覆盖所有列和当前所有数据行
      const filterRange = new GC.Spread.Sheets.Range(0, 0, dataRowCount + 1, colCount)
      const rowFilter = new GC.Spread.Sheets.Filter.HideRowFilter(filterRange)
      this.sheet.rowFilter(rowFilter)
    },

    // 初始化多sheet（在SpreadJS内部创建多个sheet页）
    initMultiSheet(availableSheets) {
      console.log('[SpreadJS] 开始在Workbook中创建多个sheet页')
      console.log('[SpreadJS] 可用sheets:', availableSheets)

      if (!this.spread || !availableSheets || availableSheets.length === 0) {
        console.warn('[SpreadJS] spread未初始化或没有sheet数据')
        return
      }

      const GC = window.GC
      if (!GC || !GC.Spread || !GC.Spread.Sheets) {
        console.error('[SpreadJS] SpreadJS未正确加载')
        return
      }

      // 清除现有的所有sheet
      const sheetCount = this.spread.getSheetCount()
      console.log(`[SpreadJS] 清除现有的${sheetCount}个sheet`)
      for (let i = sheetCount - 1; i >= 0; i--) {
        this.spread.removeSheet(i)
      }

      // 为每个字典项创建一个sheet
      availableSheets.forEach((sheetInfo, index) => {
        if (!sheetInfo || !sheetInfo.name) {
          console.error(`[SpreadJS] Sheet信息无效，索引: ${index}`, sheetInfo)
          return
        }
        
        const sheetName = sheetInfo.name // 字典标签，如"待招聘"
        console.log(`[SpreadJS] 创建sheet[${index}]: ${sheetName}`)
        
        try {
          // 创建新的worksheet
          const sheet = new GC.Spread.Sheets.Worksheet(sheetName)
          if (!sheet) {
            console.error(`[SpreadJS] 创建sheet失败，索引: ${index}`)
            return
          }
          
          this.spread.addSheet(index, sheet)
          
          // 设置基本配置
          sheet.options.isProtected = false
          sheet.options.clipBoardOptions = GC.Spread.Sheets.ClipboardPasteOptions.all
          
          // 设置列配置（所有sheet都需要相同的列结构）
          if (this.columns && this.columns.length > 0) {
            this.setupColumnsForSheet(sheet, index)
          }
          
          // 绑定事件（每个sheet都需要独立监听）
          this.bindSheetEvents(sheet, index)
          
        } catch (e) {
          console.error(`[SpreadJS] 创建sheet[${index}]失败:`, e)
        }
      })

      // 监听sheet切换事件
      this.spread.bind(GC.Spread.Sheets.Events.ActiveSheetChanged, (sender, args) => {
        console.log(`[SpreadJS] ==================== Sheet切换事件开始 ====================`)
        console.log(`[SpreadJS] 事件参数:`, args)
        
        if (!args || !args.newSheet) {
          console.error('[SpreadJS] Sheet切换事件参数无效:', args)
          return
        }
        
        // args.newSheet 和 args.oldSheet 是 Sheet 对象，不是索引
        const newSheet = args.newSheet
        const oldSheet = args.oldSheet
        
        // console.log(`[SpreadJS] 新sheet对象:`, newSheet)
        // console.log(`[SpreadJS] 旧sheet对象:`, oldSheet)
        
        if (!this.spread) {
          console.error('[SpreadJS] spread对象未初始化')
          return
        }
        
        // 检查新sheet是否有效
        if (!newSheet || typeof newSheet.name !== 'function') {
          console.error('[SpreadJS] 新sheet对象无效:', newSheet)
          return
        }
        
        let sheetName = 'Unknown'
        let newSheetIndex = -1
        
        try {
          sheetName = newSheet.name()
          // 获取sheet在workbook中的索引
          const sheetCount = this.spread.getSheetCount()
          for (let i = 0; i < sheetCount; i++) {
            const sheet = this.spread.getSheet(i)
            if (sheet === newSheet) {
              newSheetIndex = i
              break
            }
          }
        } catch (e) {
          console.error('[SpreadJS] 获取sheet信息失败:', e)
          return
        }
        
        console.log(`[SpreadJS] Sheet名称: ${sheetName}, 索引: ${newSheetIndex}`)
        
        // 更新当前活动sheet引用
        this.sheet = newSheet
        console.log(`[SpreadJS] 已更新当前sheet引用`)
        
        // 检查新sheet是否为空，如果是空的需要重新设置列结构
        let currentRowCount = 0
        let currentColCount = 0
        
        try {
          currentRowCount = newSheet.getRowCount()
          currentColCount = newSheet.getColumnCount()
          console.log(`[SpreadJS] 新sheet当前尺寸: ${currentRowCount}行 x ${currentColCount}列`)
        } catch (e) {
          console.error('[SpreadJS] 获取sheet尺寸失败:', e)
          return
        }
        
        // 如果新sheet的列数不够，重新设置列结构
        if (this.columns && currentColCount < this.columns.length) {
          console.log(`[SpreadJS] 新sheet列数不足(${currentColCount} < ${this.columns.length})，重新设置列结构`)
          try {
            this.setupColumnsForSheet(newSheet, newSheetIndex >= 0 ? newSheetIndex : 0)
          } catch (e) {
            console.error('[SpreadJS] 设置列结构失败:', e)
          }
        }
        
        // 通知父组件sheet已切换，需要加载对应数据
        console.log(`[SpreadJS] 发射sheet-changed事件: ${sheetName}`)
        try {
          this.$emit('sheet-changed', sheetName)
        } catch (e) {
          console.error('[SpreadJS] 发射sheet-changed事件失败:', e)
        }
        console.log(`[SpreadJS] ==================== Sheet切换事件结束 ====================`)
      })

      // 激活第一个sheet，但不触发切换事件（避免重复加载）
      if (availableSheets.length > 0) {
        try {
          this.spread.setActiveSheetIndex(0)
          this.sheet = this.spread.getActiveSheet()
          
          if (!this.sheet) {
            console.error('[SpreadJS] 无法获取激活的第一个sheet')
            return
          }
          
          console.log(`[SpreadJS] ✓ 激活第一个sheet: ${availableSheets[0].name}`)
          
          // 手动通知父组件加载第一个sheet的数据
          console.log(`[SpreadJS] 手动触发第一个sheet数据加载: ${availableSheets[0].name}`)
          this.$emit('sheet-changed', availableSheets[0].name)
        } catch (e) {
          console.error('[SpreadJS] 激活第一个sheet失败:', e)
        }
      }

      console.log('[SpreadJS] 多sheet创建完成，当前sheet数量:', this.spread.getSheetCount())
    },

    // 为指定sheet设置列配置
    setupColumnsForSheet(sheet, sheetIndex) {
      console.log(`[SpreadJS] 开始为Sheet${sheetIndex}设置列配置`)
      
      if (!sheet) {
        console.error('[SpreadJS] sheet参数为空，无法设置列配置')
        return
      }
      
      if (!this.columns || this.columns.length === 0) {
        console.error('[SpreadJS] 列配置为空，无法设置列配置')
        return
      }

      const GC = window.GC
      if (!GC || !GC.Spread || !GC.Spread.Sheets) {
        console.error('[SpreadJS] SpreadJS对象未正确初始化')
        return
      }
      
      // 暂停绘制以提高性能
      try {
        sheet.suspendPaint()
      } catch (e) {
        console.error('[SpreadJS] 暂停绘制失败:', e)
        return
      }

      try {
        // 设置列数
        sheet.setColumnCount(this.columns.length)

        // 设置表头和列配置
        this.columns.forEach((col, index) => {
          // 设置表头
          sheet.setValue(0, index, col.fieldLabel)
          
          // 设置列宽
          if (col.width && col.width > 0) {
            sheet.setColumnWidth(index, col.width)
            console.log(`[SpreadJS] ✓ 列${index} "${col.fieldLabel}" 宽度设置为: ${col.width}`)
          } else {
            console.log(`[SpreadJS] ⚠️ 列${index} "${col.fieldLabel}" 宽度无效(${col.width})，使用默认宽度`)
          }
          
          // 恢复单元格类型（从cellTypeConfig反序列化）
          if (col.cellTypeConfig) {
            try {
              const config = JSON.parse(col.cellTypeConfig)
              
              // ComboBox下拉列表
              if (config.type === 'combobox' && config.items) {
                const comboBox = new GC.Spread.Sheets.CellTypes.ComboBox()
                
                // 确保 items 是字符串数组（只包含 text，不包含 value）
                const textItems = Array.isArray(config.items) ? config.items : []
                comboBox.items(textItems)
                comboBox.editable(config.editable !== false) // 默认可编辑
                
                // 为整列设置ComboBox（从第2行开始，不包含表头）
                for (let row = 1; row < 10000; row++) {
                  sheet.setCellType(row, index, comboBox, GC.Spread.Sheets.SheetArea.viewport)
                }
                
                console.log(`[SpreadJS] Sheet${sheetIndex} 列 ${col.fieldLabel} 恢复ComboBox，选项:`, config.items, '可编辑:', config.editable)
              }
              // CheckBox复选框
              else if (config.type === 'checkbox') {
                const checkBox = new GC.Spread.Sheets.CellTypes.CheckBox()
                if (config.textTrue) checkBox.textTrue(config.textTrue)
                if (config.textFalse) checkBox.textFalse(config.textFalse)
                
                for (let row = 1; row < 10000; row++) {
                  sheet.setCellType(row, index, checkBox, GC.Spread.Sheets.SheetArea.viewport)
                }
                
                console.log(`[SpreadJS] Sheet${sheetIndex} 列 ${col.fieldLabel} 恢复CheckBox`)
              }
            } catch (e) {
              console.error(`[SpreadJS] 恢复列 ${col.fieldLabel} 的单元格类型失败:`, e)
            }
          }

          // 设置列数据格式
          if (col.fieldType === 'int' || col.fieldType === 'bigint') {
            // 设置数字格式
            const style = new GC.Spread.Sheets.Style()
            style.formatter = '0' // 整数格式
            style.hAlign = GC.Spread.Sheets.HorizontalAlign.right
            for (let row = 1; row < 1000; row++) {
              sheet.setStyle(row, index, style)
            }
          } else if (col.fieldType === 'decimal' || col.fieldType === 'double') {
            // 设置小数格式
            const style = new GC.Spread.Sheets.Style()
            style.formatter = '0.00' // 两位小数
            style.hAlign = GC.Spread.Sheets.HorizontalAlign.right
            for (let row = 1; row < 1000; row++) {
              sheet.setStyle(row, index, style)
            }
          }
        })

        // 设置表头样式（蓝底白字）
        const headerStyle = new GC.Spread.Sheets.Style()
        headerStyle.backColor = '#4472C4'
        headerStyle.foreColor = '#FFFFFF'
        headerStyle.font = 'bold 12px Arial'
        headerStyle.hAlign = GC.Spread.Sheets.HorizontalAlign.center
        headerStyle.vAlign = GC.Spread.Sheets.VerticalAlign.center
        
        for (let col = 0; col < this.columns.length; col++) {
          sheet.setStyle(0, col, headerStyle)
        }

        // 设置表头行高（第0行）
        sheet.setRowHeight(0, 30)

      } finally {
        try {
          sheet.resumePaint()
        } catch (e) {
          console.error('[SpreadJS] 恢复绘制失败:', e)
        }
      }
      
      console.log(`[SpreadJS] Sheet${sheetIndex}列配置设置完成`)
    },

    // 为指定sheet绑定事件
    bindSheetEvents(sheet, sheetIndex) {
      console.log(`[SpreadJS] 开始为Sheet${sheetIndex}绑定事件`)
      
      if (!sheet) {
        console.error('[SpreadJS] sheet参数为空，无法绑定事件')
        return
      }

      const GC = window.GC
      if (!GC || !GC.Spread || !GC.Spread.Sheets || !GC.Spread.Sheets.Events) {
        console.error('[SpreadJS] SpreadJS事件对象未正确初始化')
        return
      }

      try {
        // 监听数据变化
        sheet.bind(GC.Spread.Sheets.Events.CellChanged, this.onCellChanged)

        // 监听编辑结束，自动增加行数
        sheet.bind(GC.Spread.Sheets.Events.EditEnded, this.onEditEnded)

        // 拦截编辑：根据需要将表头（第0行）设为只读
        sheet.bind(GC.Spread.Sheets.Events.EditStarting, (sender, args) => {
          if (this.headerReadonly && args && args.row === 0) {
            args.cancel = true
          }
        })

        // 监听粘贴事件，处理附件单元格的粘贴
        sheet.bind(GC.Spread.Sheets.Events.ClipboardPasted, this.onClipboardPasted)
        
        // 监听单元格点击事件，处理附件下载
        sheet.bind(GC.Spread.Sheets.Events.CellClick, this.onCellClick)

        // 绑定分页滚动事件（每个sheet都需要独立绑定）
        if (this.enablePagination) {
          console.log(`[SpreadJS] 为Sheet${sheetIndex}绑定分页滚动事件`)
          sheet.bind(GC.Spread.Sheets.Events.TopRowChanged, this.checkAndLoadMore) // 滚动条/键盘导航
          sheet.bind(GC.Spread.Sheets.Events.SelectionChanged, this.checkAndLoadMore) // 选中行变化（包括END键）
        }

        console.log(`[SpreadJS] Sheet${sheetIndex} 事件绑定完成`)
      } catch (e) {
        console.error(`[SpreadJS] Sheet${sheetIndex} 事件绑定失败:`, e)
      }
    },

    // 更新当前sheet的数据（用于sheet切换后加载新数据）
    updateCurrentSheetData(data, total = null) {
      console.log('[SpreadJS] ==================== 更新Sheet数据 ====================')
      console.log('[SpreadJS] 数据条数:', data ? data.length : 0)
      console.log('[SpreadJS] 总数据量:', total)
      console.log('[SpreadJS] 数据内容:', data)
      
      if (!this.sheet) {
        console.error('[SpreadJS] 当前sheet未初始化')
        return
      }

      console.log('[SpreadJS] 当前活动sheet名称:', this.sheet.name())
      console.log('[SpreadJS] 当前sheet尺寸:', this.sheet.getRowCount(), 'x', this.sheet.getColumnCount())
      console.log('[SpreadJS] 期望列数:', this.columns ? this.columns.length : 0)

      // 暂停绘制以提高性能
      this.sheet.suspendPaint()

      try {
        // 确保sheet有正确的列结构
        if (!this.columns || this.columns.length === 0) {
          console.error('[SpreadJS] 列配置为空，无法更新数据')
          return
        }

        const expectedCols = this.columns.length
        const currentCols = this.sheet.getColumnCount()
        
        if (currentCols < expectedCols) {
          console.log(`[SpreadJS] 当前sheet列数不足(${currentCols} < ${expectedCols})，重新设置列结构`)
          this.sheet.setColumnCount(expectedCols)
          
          // 重新设置表头和列配置
          this.columns.forEach((col, index) => {
            this.sheet.setValue(0, index, col.fieldLabel)
            if (col.width && col.width > 0) {
              this.sheet.setColumnWidth(index, col.width)
              console.log(`[SpreadJS] ✓ 更新模式-列${index} "${col.fieldLabel}" 宽度设置为: ${col.width}`)
            } else {
              console.log(`[SpreadJS] ⚠️ 更新模式-列${index} "${col.fieldLabel}" 宽度无效(${col.width})，使用默认宽度`)
            }
          })
          
          // 设置表头样式（蓝底白字）
          const GC = window.GC
          const headerStyle = new GC.Spread.Sheets.Style()
          headerStyle.backColor = '#4472C4'
          headerStyle.foreColor = '#FFFFFF'
          headerStyle.font = 'bold 12px Arial'
          headerStyle.hAlign = GC.Spread.Sheets.HorizontalAlign.center
          headerStyle.vAlign = GC.Spread.Sheets.VerticalAlign.center
          
          for (let col = 0; col < this.columns.length; col++) {
            this.sheet.setStyle(0, col, headerStyle)
          }
          
          // 设置表头行高（第0行）
          this.sheet.setRowHeight(0, 30)
        }

        // 清空现有数据（保留表头，从第1行开始清空）
        let maxRow = 0
        let maxCol = 0
        
        try {
          maxRow = this.sheet.getRowCount()
          maxCol = this.sheet.getColumnCount()
          console.log(`[SpreadJS] 清空数据区域: 从第1行到第${maxRow}行，共${maxCol}列`)
        } catch (e) {
          console.error('[SpreadJS] 获取sheet尺寸失败:', e)
          return
        }
        
        // 清空数据行（保留第0行表头）
        try {
          for (let row = 1; row < maxRow; row++) {
            for (let col = 0; col < maxCol; col++) {
              this.sheet.setValue(row, col, null)
              // 清空单元格的tag和样式（如果有附件等）
              this.sheet.setTag(row, col, null)
            }
          }
        } catch (e) {
          console.error('[SpreadJS] 清空数据失败:', e)
          return
        }

        // 加载新数据
        if (data && data.length > 0) {
          console.log('[SpreadJS] 开始加载新数据...')
          try {
            this.loadData(data)
          } catch (e) {
            console.error('[SpreadJS] 加载新数据失败:', e)
          }
        } else {
          console.log('[SpreadJS] 没有新数据需要加载，保持空的模板结构')
        }

        // 更新分页状态 - 支持多sheet分页
        this.loadedData = data || []
        this.currentPage = 1
        
        // 如果提供了total，说明这是分页数据，需要支持继续加载
        if (total !== null && total !== undefined) {
          this.totalRecords = total
          this.totalPages = Math.ceil(total / this.pageSize) // 设置总页数
          this.allDataLoaded = (data && data.length >= total) // 只有当前数据量达到总量时才标记为全部加载
          console.log(`[SpreadJS] 分页模式 - 当前加载: ${data ? data.length : 0}, 总计: ${total}, 总页数: ${this.totalPages}, 全部加载: ${this.allDataLoaded}`)
        } else {
          // 没有提供total，按照原有逻辑处理
          this.allDataLoaded = false
          this.totalPages = 999 // 设置一个较大的值，确保分页可用
          console.log('[SpreadJS] 未提供总数，保持分页可用状态')
        }

      } finally {
        try {
          if (this.sheet) {
            this.sheet.resumePaint()
          }
        } catch (e) {
          console.error('[SpreadJS] 更新sheet数据时恢复绘制失败:', e)
        }
      }

      console.log('[SpreadJS] ✓ 当前sheet数据更新完成')
      console.log('[SpreadJS] ==================== Sheet数据更新完成 ====================')
    }

  }
}
</script>

<style scoped>
.spreadjs-container {
  width: 100%;
  height: 100%;
  min-height: 500px;
  position: relative;
}

.spreadjs-loading {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  color: #409EFF;
}

.spreadjs-loading i {
  font-size: 40px;
  margin-bottom: 10px;
}

.spreadjs-loading p {
  font-size: 14px;
  color: #666;
}

.spreadjs-host {
  width: 100%;
  height: 100%;
}
</style>

